using System;
using System.Collections.Generic;
using System.Linq;
using QFramework.NodeGraph;
using UnityEngine;
using Random = UnityEngine.Random;

namespace QFramework.Gun
{
    [DisallowMultipleComponent]
    public class DungeonBuilder : MonoSingleton<DungeonBuilder>
    {

        // 地下城生成器的房间字典（键为房间ID，值为房间对象）
        public Dictionary<string, Room> dungeonBuilderRoomDictionary = new Dictionary<string, Room>();
    
        // 房间模板字典（键为房间模板的GUID，值为房间模板对象）
        private Dictionary<string, RoomTemplateSO> roomTemplateDictionary = new Dictionary<string, RoomTemplateSO>();

        // 当前关卡的房间模板列表
        private List<RoomTemplateSO> roomTemplateList = null;

        // 房间节点类型列表
        public RoomNodeTypeListSO roomNodeTypeList;

        // 地下城构建是否成功的标志位
        private bool dungeonBuildSuccessful;

        private void Awake()
        {
            //TODO:后续在GameApp中设置帧率
            //Application.targetFrameRate = 60;
            
        }

        private void OnEnable()
        {
            GameManager.Instance.dimmedMaterial.SetFloat("Alpha_Slider", 0f);
        }
        private void OnDisable()
        {
            GameManager.Instance.dimmedMaterial.SetFloat("Alpha_Slider", 1f);
        }

        #region 地下城随机生成主方法

        /// <summary>
        /// 生成随机地下城，如果生成成功返回true，生成失败返回false
        /// </summary>
        public bool GenerateDungeon(DungeonLevelSO currentDungeonLevel)
        {
            roomTemplateList = currentDungeonLevel.roomTemplateList;

            // 将房间模板脚本对象加载到字典中
            LoadRoomTemplatesIntoDictionary();

            dungeonBuildSuccessful = false;
            int dungeonBuildAttempts = 0;

            // 当地下城未生成成功且尝试次数小于最大生成次数时，继续尝试
            while (!dungeonBuildSuccessful && dungeonBuildAttempts < Settings.maxDungeonBuildAttempts)
            {
                dungeonBuildAttempts++;

                // 从列表中随机选择一个房间节点图
                RoomNodeGraphSO roomNodeGraph = SelectRandomRoomNodeGraph(currentDungeonLevel.roomNodeGraphList);

                int dungeonRebuildAttemptsForNodeGraph = 0;
                dungeonBuildSuccessful = false;

                // 循环尝试构建地下城，直到成功或超过最大尝试次数
                while (!dungeonBuildSuccessful && dungeonRebuildAttemptsForNodeGraph <= Settings.maxDungeonRebuildAttemptsForRoomGraph)
                {
                    // 清理地下城的房间游戏对象和房间字典
                    ClearDungeon();

                    dungeonRebuildAttemptsForNodeGraph++;

                    // 尝试为选定的房间节点图构建一个随机地下城
                    dungeonBuildSuccessful = AttemptToBuildRandomDungeon(roomNodeGraph);
                }

                // 如果生成成功，实例化房间游戏对象
                if (dungeonBuildSuccessful)
                {
                    InstantiateRoomGameobjects();
                }
            }

            return dungeonBuildSuccessful;
        }

        /// <summary>
        /// 尝试根据指定的房间节点图随机生成地下城布局。如果成功生成随机布局，则返回 true；
        /// 如果遇到问题，需要再次尝试，则返回 false。
        /// </summary>
        private bool AttemptToBuildRandomDungeon(RoomNodeGraphSO roomNodeGraph)
        {
            // 创建开放的房间节点队列
            Queue<RoomNodeSO> openRoomNodeQueue = new Queue<RoomNodeSO>();

            // 从房间节点图中找到入口节点，并将其添加到房间节点队列中
            RoomNodeSO entranceNode = roomNodeGraph.GetRoomNodeByType(roomNodeTypeList.roomNodeTypes.Find(x => x.isEntrance));

            if (entranceNode != null)
            {
                openRoomNodeQueue.Enqueue(entranceNode);
            }
            else
            {
                Debug.Log("未找到入口节点");
                return false;  // 地下城未生成
            }

            // 开始时假设没有房间重叠
            bool noRoomOverlaps = true;

            // 处理开放的房间节点队列
            noRoomOverlaps = ProcessRoomsInOpenRoomNodeQueue(roomNodeGraph, openRoomNodeQueue, noRoomOverlaps);

            // 如果所有房间节点都已处理且没有房间重叠，则返回 true
            if (openRoomNodeQueue.Count == 0 && noRoomOverlaps)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        
        /// <summary>
        /// 处理开放的房间节点队列，如果没有房间重叠，则返回 true
        /// </summary>
        private bool ProcessRoomsInOpenRoomNodeQueue(RoomNodeGraphSO roomNodeGraph, Queue<RoomNodeSO> openRoomNodeQueue,
            bool noRoomOverlaps)
        {
            // 当开放的房间节点队列中还有节点且未检测到房间重叠时，继续循环处理
            while (openRoomNodeQueue.Count > 0 && noRoomOverlaps == true)
            {
                // 从开放的房间节点队列中获取下一个房间节点
                RoomNodeSO roomNode = openRoomNodeQueue.Dequeue();

                // 将房间节点图中与当前父房间节点相连的所有子节点添加到队列中
                foreach (RoomNodeSO childRoomNode in roomNodeGraph.GetChildNodes(roomNode))
                {
                    openRoomNodeQueue.Enqueue(childRoomNode);
                }

                // 如果当前房间节点是入口节点，则标记为已放置，并将其添加到房间字典中
                if (roomNode.roomNodeType.isEntrance)
                {
                    RoomTemplateSO roomTemplate = GetRandomRoomTemplate(roomNode.roomNodeType);

                    Room room = CreateRoomFromRoomTemplate(roomTemplate, roomNode);

                    room.isPositioned = true;

                    // 将房间添加到房间字典中
                    dungeonBuilderRoomDictionary.Add(room.id, room);
                    
                    GameManager.Instance.SetCurrentRoom(room);
                }
                // 如果当前房间节点不是入口节点
                else
                {
                    // 获取该节点的父节点对应的房间
                    Room parentRoom = dungeonBuilderRoomDictionary[roomNode.parentRoomNodeIDList[0]];

                    // 检查当前房间是否可以放置且不与其他房间重叠
                    noRoomOverlaps = CanPlaceRoomWithNoOverlaps(roomNode, parentRoom);
                }
            }

            return noRoomOverlaps;
        }
        
        #endregion

        #region 重叠检查与放置

        /// <summary>
        /// 尝试将房间节点放置在地下城中 - 如果房间可以成功放置且不与其他房间重叠，则返回 true，否则返回 false
        /// </summary>
        private bool CanPlaceRoomWithNoOverlaps(RoomNodeSO roomNode, Room parentRoom)
        {
            // 初始化房间重叠标志位，默认假设房间重叠，直到证明不存在重叠
            bool roomOverlaps = true;

            // 当房间存在重叠时，尝试将当前房间放置在父房间的所有可用门口中，直到房间成功放置且不重叠为止
            while (roomOverlaps)
            {
                // 随机选择父房间中尚未连接的可用门口
                List<Doorway> unconnectedAvailableParentDoorways = GetUnconnectedAvailableDoorways(parentRoom.doorWayList).ToList();

                // 如果父房间没有更多可用的门口，则放置失败（房间重叠）
                if (unconnectedAvailableParentDoorways.Count == 0)
                {
                    return false; // 房间重叠，放置失败
                }

                // 从未连接的可用门口中随机选择一个门口作为父门口
                Doorway doorwayParent = unconnectedAvailableParentDoorways[UnityEngine.Random.Range(0, unconnectedAvailableParentDoorways.Count)];

                // 根据父门口的方向，获取一个与之方向一致的随机房间模板
                RoomTemplateSO roomtemplate = GetRandomTemplateForRoomConsistentWithParent(roomNode, doorwayParent);

                // 根据房间模板和房间节点创建一个房间对象
                Room room = CreateRoomFromRoomTemplate(roomtemplate, roomNode);

                // 放置房间 - 如果房间不重叠，则返回 true
                if (PlaceTheRoom(parentRoom, doorwayParent, room))
                {
                    // 如果房间不重叠，则设置 roomOverlaps 为 false 以退出循环
                    roomOverlaps = false;

                    // 标记房间为已放置
                    room.isPositioned = true;

                    // 将房间添加到地下城房间字典中
                    dungeonBuilderRoomDictionary.Add(room.id, room);
                }
                else
                {
                    // 如果房间重叠，则继续循环尝试放置房间
                    roomOverlaps = true;
                }
            }

            return true;  // 无房间重叠，放置成功
        }
        
        /// <summary>
        /// 放置房间 - 如果房间没有重叠则返回 true，否则返回 false
        /// </summary>
        private bool PlaceTheRoom(Room parentRoom, Doorway doorwayParent, Room room)
        {
            // 获取当前房间中与父房间门口方向相反的门口位置
            Doorway doorway = GetOppositeDoorway(doorwayParent, room.doorWayList);

            // 如果当前房间中没有与父房间相反方向的门口，则返回 false
            if (doorway == null)
            {
                // 将父房间的门口标记为不可用，以避免再次尝试连接
                doorwayParent.isUnavailable = true;

                return false;
            }

            // 计算父房间门口在世界网格中的位置
            Vector2Int parentDoorwayPosition = parentRoom.lowerBounds + doorwayParent.position - parentRoom.templateLowerBounds;

            Vector2Int adjustment = Vector2Int.zero;

            // 根据当前房间门口的位置方向，计算房间位置的调整偏移量
            switch (doorway.orientation)
            {
                case Orientation.north:
                    adjustment = new Vector2Int(0, -1);
                    break;

                case Orientation.east:
                    adjustment = new Vector2Int(-1, 0);
                    break;

                case Orientation.south:
                    adjustment = new Vector2Int(0, 1);
                    break;

                case Orientation.west:
                    adjustment = new Vector2Int(1, 0);
                    break;

                case Orientation.none:
                    break;

                default:
                    break;
            }

            // 计算房间的下边界和上边界位置，使其与父房间的门口对齐
            room.lowerBounds = parentDoorwayPosition + adjustment + room.templateLowerBounds - doorway.position;
            room.upperBounds = room.lowerBounds + room.templateUpperBounds - room.templateLowerBounds;

            // 检查房间是否与其他已放置的房间重叠
            Room overlappingRoom = CheckForRoomOverlap(room);

            if (overlappingRoom == null)
            {
                // 标记门口为已连接且不可用
                doorwayParent.isConnected = true;
                doorwayParent.isUnavailable = true;

                doorway.isConnected = true;
                doorway.isUnavailable = true;

                // 返回 true 表示房间成功放置且没有重叠
                return true;
            }
            else
            {
                // 将父房间的门口标记为不可用，以避免再次尝试连接
                doorwayParent.isUnavailable = true;

                return false;
            }
        }

        /// <summary>
        /// 从门口列表中获取与父房间门口方向相反的门口
        /// </summary>
        private Doorway GetOppositeDoorway(Doorway parentDoorway, List<Doorway> doorwayList)
        {
            // 遍历门口列表，查找与父房间门口方向相反的门口
            foreach (Doorway doorwayToCheck in doorwayList)
            {
                if (parentDoorway.orientation == Orientation.east && doorwayToCheck.orientation == Orientation.west)
                {
                    return doorwayToCheck;
                }
                else if (parentDoorway.orientation == Orientation.west && doorwayToCheck.orientation == Orientation.east)
                {
                    return doorwayToCheck;
                }
                else if (parentDoorway.orientation == Orientation.north && doorwayToCheck.orientation == Orientation.south)
                {
                    return doorwayToCheck;
                }
                else if (parentDoorway.orientation == Orientation.south && doorwayToCheck.orientation == Orientation.north)
                {
                    return doorwayToCheck;
                }
            }

            return null; // 如果未找到相反方向的门口，则返回 null
        }

        /// <summary>
        /// 检查房间是否与其他房间重叠，如果存在重叠的房间则返回该房间，否则返回 null
        /// </summary>
        private Room CheckForRoomOverlap(Room roomToTest)
        {
            // 遍历所有已放置的房间
            foreach (KeyValuePair<string, Room> keyvaluepair in dungeonBuilderRoomDictionary)
            {
                Room room = keyvaluepair.Value;

                // 如果当前房间是测试房间本身或该房间未被放置，则跳过
                if (room.id == roomToTest.id || !room.isPositioned)
                    continue;

                // 如果房间与其他房间重叠，则返回该重叠的房间
                if (IsOverLappingRoom(roomToTest, room))
                {
                    return room;
                }
            }

            // 如果没有发现重叠的房间，则返回 null
            return null;
        }

        /// <summary>
        /// 检查两个房间是否重叠 - 如果它们重叠返回 true，否则返回 false
        /// </summary>
        private bool IsOverLappingRoom(Room room1, Room room2)
        {
            // 检查房间在 X 轴上的重叠情况
            bool isOverlappingX = IsOverLappingInterval(room1.lowerBounds.x, room1.upperBounds.x, room2.lowerBounds.x, room2.upperBounds.x);

            // 检查房间在 Y 轴上的重叠情况
            bool isOverlappingY = IsOverLappingInterval(room1.lowerBounds.y, room1.upperBounds.y, room2.lowerBounds.y, room2.upperBounds.y);

            // 如果两个房间在 X 轴和 Y 轴上均发生重叠，则返回 true
            if (isOverlappingX && isOverlappingY)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        
        /// <summary>
        /// 检查两个区间是否发生重叠 - 如果重叠返回 true，否则返回 false（此方法由 IsOverLappingRoom 方法调用）
        /// </summary>
        private bool IsOverLappingInterval(int imin1, int imax1, int imin2, int imax2)
        {
            // 如果区间 [imin1, imax1] 与区间 [imin2, imax2] 有重叠，则返回 true
            if (Mathf.Max(imin1, imin2) <= Mathf.Min(imax1, imax2))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        
        /// <summary>
        /// 获取未连接且未禁用的门口
        /// </summary>
        private IEnumerable<Doorway> GetUnconnectedAvailableDoorways(List<Doorway> roomDoorwayList)
        {
            // Loop through doorway list
            foreach (Doorway doorway in roomDoorwayList)
            {
                if (!doorway.isConnected && !doorway.isUnavailable)
                    yield return doorway;
            }
        }
        

        #endregion
        
        
        /// <summary>
        /// 获取一个随机的房间节点图
        /// </summary>
        /// <param name="roomNodeGraphList"></param>
        /// <returns></returns>
        private RoomNodeGraphSO SelectRandomRoomNodeGraph(List<RoomNodeGraphSO> roomNodeGraphList)
        {
            return  roomNodeGraphList[Random.Range(0, roomNodeGraphList.Count)];
        }

        /// <summary>
        /// 将房间模板加载到字典中
        /// </summary>
        private void LoadRoomTemplatesIntoDictionary()
        {
            // 清空房间模板字典
            roomTemplateDictionary.Clear();

            // 将房间模板列表中的模板加载到字典中
            foreach (RoomTemplateSO roomTemplate in roomTemplateList)
            {
                if (!roomTemplateDictionary.ContainsKey(roomTemplate.guid))
                {
                    roomTemplateDictionary.Add(roomTemplate.guid, roomTemplate);
                }
                else
                {
                    Debug.Log("在房间模板列表中存在重复的房间模板键值");
                }
            }
        }
        
        
        /// <summary>
        /// 从房间模板列表中获取一个与指定房间类型匹配的随机房间模板并返回
        /// （如果未找到匹配的房间模板，则返回 null）。
        /// </summary>
        private RoomTemplateSO GetRandomRoomTemplate(RoomNodeTypeSO roomNodeType)
        {
            // 创建一个存储匹配房间模板的列表
            List<RoomTemplateSO> matchingRoomTemplateList = new List<RoomTemplateSO>();

            // 遍历房间模板列表
            foreach (RoomTemplateSO roomTemplate in roomTemplateList)
            {
                // 如果房间模板的节点类型与传入的房间节点类型匹配，则将该模板添加到匹配列表中
                if (roomTemplate.roomNodeType == roomNodeType)
                {
                    matchingRoomTemplateList.Add(roomTemplate);
                }
            }

            // 如果匹配列表为空，则返回 null
            if (matchingRoomTemplateList.Count == 0)
                return null;

            // 从匹配的房间模板列表中随机选择一个模板并返回
            return matchingRoomTemplateList[UnityEngine.Random.Range(0, matchingRoomTemplateList.Count)];
        }

        /// <summary>
        /// 根据房间模板和房间节点创建房间，并返回创建的房间对象
        /// </summary>
        private Room CreateRoomFromRoomTemplate(RoomTemplateSO roomTemplate, RoomNodeSO roomNode)
        {
            // 根据模板初始化房间对象
            Room room = new Room();

            room.templateID = roomTemplate.guid; // 设置房间模板的 GUID
            room.id = roomNode.id; // 设置房间节点的 ID
            room.prefab = roomTemplate.prefab; // 设置房间的预制体
            room.roomNodeType = roomTemplate.roomNodeType; // 设置房间节点的类型
            room.lowerBounds = roomTemplate.lowerBounds; // 设置房间的下边界
            room.upperBounds = roomTemplate.upperBounds; // 设置房间的上边界
            room.spawnPositionArray = roomTemplate.spawnPositionArray; // 设置房间内的生成位置数组
            room.templateLowerBounds = roomTemplate.lowerBounds; // 设置模板的下边界
            room.templateUpperBounds = roomTemplate.upperBounds; // 设置模板的上边界
            room.childRoomIDList = CopyStringList(roomNode.childRoomNodeIDList); // 复制房间节点的子节点ID列表
            room.doorWayList = CopyDoorwayList(roomTemplate.doorwayList); // 复制房间模板的门口列表

            // 设置房间的父节点ID
            if (roomNode.parentRoomNodeIDList.Count == 0) // 如果房间节点没有父节点（说明是入口节点）
            {
                room.parentRoomID = ""; // 设置为空字符串
                room.isPreviouslyVisited = true; // 标记该房间为已访问
            }
            else
            {
                room.parentRoomID = roomNode.parentRoomNodeIDList[0]; // 否则设置为父节点ID列表中的第一个ID
            }

            return room; // 返回创建的房间对象
        }

        /// <summary>
        /// 根据父节点门口的方向获取与之方向一致的随机房间模板
        /// </summary>
        private RoomTemplateSO GetRandomTemplateForRoomConsistentWithParent(RoomNodeSO roomNode, Doorway doorwayParent)
        {
            RoomTemplateSO roomtemplate = null;

            // 如果房间节点是走廊类型，则根据父节点门口方向选择合适的走廊模板
            if (roomNode.roomNodeType.isCorridor)
            {
                switch (doorwayParent.orientation)
                {
                    case Orientation.north:
                    case Orientation.south:
                        // 如果父门口方向是北或南，选择南北走廊的房间模板
                        roomtemplate = GetRandomRoomTemplate(roomNodeTypeList.roomNodeTypes.Find(x => x.isCorridorNS));
                        break;

                    case Orientation.east:
                    case Orientation.west:
                        // 如果父门口方向是东或西，选择东西走廊的房间模板
                        roomtemplate = GetRandomRoomTemplate(roomNodeTypeList.roomNodeTypes.Find(x => x.isCorridorEW));
                        break;

                    case Orientation.none:
                        break;

                    default:
                        break;
                }
            }
            // 否则，随机选择一个房间模板
            else
            {
                roomtemplate = GetRandomRoomTemplate(roomNode.roomNodeType);
            }

            return roomtemplate; // 返回选定的房间模板
        }
        
        /// <summary>
        /// 创建门口列表的深拷贝
        /// </summary>
        private List<Doorway> CopyDoorwayList(List<Doorway> oldDoorwayList)
        {
            // 创建一个新的门口列表
            List<Doorway> newDoorwayList = new List<Doorway>();

            // 遍历旧的门口列表
            foreach (Doorway doorway in oldDoorwayList)
            {
                // 为每个门口对象创建一个新的门口实例
                Doorway newDoorway = new Doorway();

                // 复制门口对象的所有属性
                newDoorway.position = doorway.position;
                newDoorway.orientation = doorway.orientation;
                newDoorway.doorPrefab = doorway.doorPrefab;
                newDoorway.isConnected = doorway.isConnected;
                newDoorway.isUnavailable = doorway.isUnavailable;
                newDoorway.doorwayStartCopyPosition = doorway.doorwayStartCopyPosition;
                newDoorway.doorwayCopyTileWidth = doorway.doorwayCopyTileWidth;
                newDoorway.doorwayCopyTileHeight = doorway.doorwayCopyTileHeight;

                // 将新的门口对象添加到新列表中
                newDoorwayList.Add(newDoorway);
            }

            return newDoorwayList; // 返回新创建的门口列表
        }

        /// <summary>
        /// 创建字符串列表的深拷贝
        /// </summary>
        private List<string> CopyStringList(List<string> oldStringList)
        {
            // 创建一个新的字符串列表
            List<string> newStringList = new List<string>();

            // 遍历旧的字符串列表，将每个字符串复制到新列表中
            foreach (string stringValue in oldStringList)
            {
                newStringList.Add(stringValue);
            }

            return newStringList; // 返回新创建的字符串列表
        }
        
        public RoomTemplateSO GetRoomTemplate(string roomTemplateID)
        {
            if (roomTemplateDictionary.TryGetValue(roomTemplateID, out RoomTemplateSO roomTemplate))
            {
                return roomTemplate;
            }
            else
            {
                return null;
            }
        }
        
        public Room GetRoomByRoomID(string roomID)
        {
            if (dungeonBuilderRoomDictionary.TryGetValue(roomID, out Room room))
            {
                return room;
            }
            else
            {
                return null;
            }
        }
        
        private void InstantiateRoomGameobjects()
        {
            foreach (KeyValuePair<string,Room> keyValuePair in dungeonBuilderRoomDictionary)
            {
                var  room = keyValuePair.Value;
                
                var roomPosition = new Vector3(room.lowerBounds.x -room.templateLowerBounds.x,
                    room.lowerBounds.y - room.templateLowerBounds.y,0);

                var instantiatedRoom = room.prefab.InstantiateWithParent(transform)
                    .Position(roomPosition).LocalRotation(Quaternion.identity).GetComponent<InstantiatedRoom>();
                instantiatedRoom.room = room;
                instantiatedRoom.Initialise();
                room.instantiatedRoom = instantiatedRoom;
            }
        }
        private void ClearDungeon()
        {
            if (dungeonBuilderRoomDictionary.Count > 0)
            {
                foreach (KeyValuePair<string, Room> keyvaluepair in dungeonBuilderRoomDictionary)
                {
                    Room room = keyvaluepair.Value;

                    if (room.instantiatedRoom != null)
                    {
                        Destroy(room.instantiatedRoom.gameObject);
                    }
                }

                dungeonBuilderRoomDictionary.Clear();
            }
        }
        
    }
    
}
